package com.qf.micrograss.grass.service.impl;

import cn.hutool.core.date.DateUtil;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.qf.api.point.feign.PointFeignService;
import com.qf.common.baidu.info.CheckResult;
import com.qf.common.baidu.utils.BaiduTextCheckUtils;
import com.qf.common.core.common.PageVO;
import com.qf.common.core.common.R;
import com.qf.common.core.constants.CommonConstants;
import com.qf.common.core.entity.Grass;
import com.qf.common.core.entity.GrassLike;
import com.qf.common.core.util.UserUtils;
import com.qf.common.minio.utils.MinioUtil;
import com.qf.common.redis.utils.RedisUtil;
import com.qf.micrograss.grass.mapper.GrassMapper;
import com.qf.micrograss.grass.param.GrassParam;
import com.qf.micrograss.grass.param.GrassSearchParam;
import com.qf.micrograss.grass.param.TodayLikeInfoParam;
import com.qf.micrograss.grass.service.GrassLikeService;
import com.qf.micrograss.grass.service.GrassService;
import com.qf.micrograss.grass.vo.GrassVO;
import io.seata.spring.annotation.GlobalTransactional;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;

import javax.annotation.Resource;
import java.io.IOException;
import java.util.*;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.stream.Collectors;

/**
 * @Classname GrassServiceImpl
 * @Description
 * @Date 2025-05-28 11:09
 * @Created by 老任与码
 */
@Service
public class GrassServiceImpl implements GrassService {

    @Resource
    private GrassMapper grassMapper;

    @Resource
    private PointFeignService pointFeignService;

    @Resource
    private GrassLikeService grassLikeService;

    @Autowired
    private RedisUtil redisUtil;

    @Resource
    private ThreadPoolExecutor threadPoolExecutor;

    @Resource
    private MinioUtil minioUtil;

    // 分布式事务的注解 ，AT模式
    @GlobalTransactional(rollbackFor = Exception.class)
    @Override
    public void addGrass(GrassParam grassParam) {
        // 如果审核不合规，直接抛出异常
        // 根据实际业务，比如，自动审核返回疑似状态，考虑考虑将数据写入数据库，后台通过人工审核
        CheckResult checkResult = BaiduTextCheckUtils.textCheck(grassParam.getTitle() + grassParam.getContent());
        if (checkResult.getConclusionType() != 1) {
            throw new RuntimeException(checkResult.getMsg());
        }

        Integer uid = UserUtils.getUid();

        Grass grass = new Grass();
        BeanUtils.copyProperties(grassParam, grass);

        // 图片路径按照逗号分隔
        String imgPath = String.join(";", grassParam.getImgList());
        // String imgPath = grassParam.getImgList().stream().collect(Collectors.joining(","));
        grass.setImagePath(imgPath);
        grass.setUid(uid);
        grassMapper.insert(grass);

        // 送积分
        R r = pointFeignService.remoteAddPoint(2);
        if (r.getCode() == CommonConstants.FAIL) {
            // 抛异常
             throw new RuntimeException("积分增加失败");
        }

        // 增加redis中当天的点赞数
        String key = String.format("likeNum:%s", DateUtil.format(new Date(), "yyyyMMdd"));
        redisUtil.incrCacheMapValue(key, grass.getId().toString(), 0);
        // 同时更新数据库中的图片路径字段
        grass.setImagePath(imgPath);
        int result = grassMapper.update(grass, Wrappers.lambdaQuery(Grass.class).eq(Grass::getId, grass.getId()));
        // 判断是否更新成功
        if (result != 1) {
             throw new RuntimeException("更新图片路径失败");
        }
    }

    @Override
    public PageVO<GrassVO> grassPage(GrassSearchParam grassSearchParam) {
        Integer uid = UserUtils.getUid();

        // 查询文章列表：就是grass表中checkStatus=1的文章
        Page<Grass> page = new Page(grassSearchParam.getPageNum(), grassSearchParam.getPageSize());
        page = grassMapper.selectPage(page, Wrappers.lambdaQuery(Grass.class)
                .eq(Grass::getCheckStatus, 1));
        List<Grass> records = page.getRecords();

        // 查询登录用户，对应的文章的点赞记录
        List<Integer> idList = records.stream().map(grass -> grass.getId()).collect(Collectors.toList());
        // 判断是否有点赞记录，如果没有 ，则返回空的PageVO，点赞数为0
        if (idList.size() == 0) {
             PageVO<GrassVO> pageVO = new PageVO<>();
             pageVO.setTotal(0L);
             pageVO.setDataList(new ArrayList<>());
             return pageVO;
        }
        List<GrassLike> grassLikes = grassLikeService.listByGrassIdsAndUid(idList, uid);

        String key = String.format("likeNum:%s", DateUtil.format(new Date(), "yyyyMMdd"));

        List<GrassVO> voList = records.stream().map(grass -> {
            // 对grass对象进行判断操作
            if (grass != null) {
                GrassVO vo = new GrassVO();
                vo.setId(grass.getId());
                vo.setTitle(grass.getTitle());


                String[] split = grass.getImagePath().split(";");

                vo.setCoverImg(split[0]);

                // 数据表中存的不是最新数据，实时的点赞总数来自数据库表的likeNum字段和reids中当天点赞数
                Integer todayLikeNum = redisUtil.getCacheMapValue(key, grass.getId().toString());
                vo.setLikeNum(todayLikeNum == null ? grass.getLikeNum() : grass.getLikeNum() + todayLikeNum);

                // 查询点赞记录中是否包含当前的文章id
                GrassLike findLike = grassLikes.stream().filter(grassLike -> grassLike.getGrassId().equals(grass.getId()))
                        .findFirst().orElse(null);
                vo.setLikeFlag(findLike == null ? 0 : 1);
                return vo;
            }
            return null;

        }).collect(Collectors.toList());

        PageVO<GrassVO> pageVO = new PageVO<>();
        pageVO.setTotal(page.getTotal());
        pageVO.setDataList(voList);

        return pageVO;
    }

    @Override
    public void updateGrassLikeNumTask() {

        // 从redis中取头一天的点赞记录
        String yesterday = DateUtil.format(DateUtil.yesterday(), "yyyyMMdd");
        Map<String, Object> cacheMap = redisUtil.getCacheMap("likeNum:" + yesterday);

        // 批处理的次数，比如200个数据，count=2; 230个数据，count=3
        int count = 0;
        int size = cacheMap.size();
        if (size % 100 == 0) {
            count = size / 100;
        } else {
            count = size / 100 + 1;
        }

        // 并发编程中，用于计数
        // 通过该方式，保证所有的更新线程执行完成，删除redis中数据
        CountDownLatch countDownLatch = new CountDownLatch(count);

        // 分批的批量更新
        List<TodayLikeInfoParam> list = new ArrayList<>();
        for (String key : cacheMap.keySet()) {
            TodayLikeInfoParam todayLikeInfoParam = new TodayLikeInfoParam();
            todayLikeInfoParam.setGrassId(Integer.valueOf(key));
            todayLikeInfoParam.setLikeNum((Integer) cacheMap.get(key));
            list.add(todayLikeInfoParam);
            if (list.size() == 100) {
                // 批量更新
                final List<TodayLikeInfoParam> finalList = list;
                threadPoolExecutor.execute(() -> {

                    try {
                        grassMapper.updateBatch(finalList);
                    } catch (Exception e) {
                        throw new RuntimeException(e);
                    } finally {
                        // countDownLatch计数值-1
                        countDownLatch.countDown();
                    }
                });
                list = new ArrayList<>();
            }
        }
        if (list.size() > 0) {
            // 批量更新
            List<TodayLikeInfoParam> finalList1 = list;
            threadPoolExecutor.execute(() -> {
                try {
                    grassMapper.updateBatch(finalList1);
                } catch (Exception e) {
                    throw new RuntimeException(e);
                } finally {
                    countDownLatch.countDown();
                }
            });
        }
        try {
            // 阻塞程序的执行，等待countdownlatch的计数减为0
            countDownLatch.await();
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }

        redisUtil.deleteObject("likeNum:" + yesterday);
    }

    @Override
    public String uploadImg(MultipartFile file) {
        // 获得原始文件名
        String filename = file.getOriginalFilename();

        //  使用UUDI自动生成的文件名作为新的文件名，防止文件名重复
        String uuid = UUID.randomUUID().toString().replace("-", "");
        filename = uuid + filename.substring(filename.lastIndexOf("."));

        // 这里需要先判断一下文件是否上传成功，如果上传成功，则返回minio的路径，如果上传失败，则抛出异常

        if (filename == null) {
            throw new RuntimeException("上传文件失败!");
        }

        // 此时，用户仅仅是上传文章的封面，还没有发表文章，因此就无法更新到后端数据库

        try {
            return minioUtil.upload(filename, file.getInputStream());
        } catch (IOException e) {
            throw new RuntimeException(e);
        }

    }
}
